use nand2tetris_vmtranslator_rust::data::Segment;

use crate::{
    data::{KeywordType, SymbolType},
    error::{CompilerError, CompilerResult},
};

/// The class's symbol table
#[derive(Debug)]
pub(super) struct SymbolTable {
    table: Vec<SymbolTableItem>,
}

impl SymbolTable {
    pub(super) fn new_tables() -> Self {
        Self { table: Vec::new() }
    }

    /// add a new row in the symbol table
    pub(super) fn add_row(
        &mut self,
        name: String,
        var_type: VariableType,
        var_kind: VariableKind,
    ) -> CompilerResult<()> {
        if self.table.iter().any(|item| item.name == name) {
            return Err(CompilerError::Symbol(format!(
                "the variable {name} has exist!"
            )));
        }
        //get the max index of the var_kind
        let index_of_kind = self
            .table
            .iter()
            .filter_map(|item| (item.var_kind == var_kind).then_some(item.index_of_kind))
            .max()
            .map(|index| index + 1)
            .unwrap_or_default();
        self.table.push(SymbolTableItem {
            name,
            var_type,
            var_kind,
            index_of_kind,
        });
        Ok(())
    }

    pub(super) fn add_var_desc(
        &mut self,
        var_kind: VariableKind,
        var_desc: VariableDesc,
    ) -> CompilerResult<()> {
        for var_name in var_desc.vars {
            self.add_row(var_name, var_desc.var_type.clone(), var_kind)?
        }
        Ok(())
    }
    /// get the symbole table item
    ///
    /// # arguments
    /// * name : the finded item's name
    pub(super) fn get_row(&self, name: &str) -> Option<&SymbolTableItem> {
        self.table.iter().find(|item| item.name == name)
    }

    /// get local variable num
    pub(super) fn get_local_variable_num(&self) -> u16 {
        self.table
            .iter()
            .filter(|item| item.var_kind == VariableKind::Var)
            .count() as u16
    }

    /// get field variable num
    pub(super) fn get_field_variable_num(&self) -> u16 {
        self.table
            .iter()
            .filter(|item| item.var_kind == VariableKind::Field)
            .count() as u16
    }
}
#[derive(Debug, Clone)]
pub(super) struct SymbolTableItem {
    pub(super) name: String,
    pub(super) var_type: VariableType,
    pub(super) var_kind: VariableKind,
    pub(super) index_of_kind: u16,
}
impl SymbolTableItem {
    /// get the variable's class name
    pub(super) fn get_class_name(&self) -> Option<&str> {
        match &self.var_type {
            VariableType::Class(name) => Some(name),
            _ => None,
        }
    }
}

/// variable kind
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(super) enum VariableKind {
    Field,
    Static,
    Arg,
    Var,
}

impl TryFrom<KeywordType> for VariableKind {
    type Error = CompilerError;

    fn try_from(value: KeywordType) -> std::result::Result<Self, Self::Error> {
        match value {
            KeywordType::Let => Ok(Self::Var),
            KeywordType::Field => Ok(Self::Field),
            KeywordType::Static => Ok(Self::Static),
            _ => Err(CompilerError::Keyword("not a var kind".into())),
        }
    }
}

impl From<VariableKind> for Segment {
    fn from(value: VariableKind) -> Self {
        match value {
            VariableKind::Field => Segment::This,
            VariableKind::Static => Segment::Static,
            VariableKind::Arg => Segment::Argument,
            VariableKind::Var => Segment::Local,
        }
    }
}

/// variable Type
#[derive(Debug, Clone)]
pub(super) enum VariableType {
    Integer,
    Boolean,
    Char,
    Class(String),
}

impl TryFrom<KeywordType> for VariableType {
    type Error = CompilerError;

    fn try_from(value: KeywordType) -> std::result::Result<Self, Self::Error> {
        match value {
            KeywordType::Int => Ok(Self::Integer),
            KeywordType::Boolean => Ok(Self::Boolean),
            KeywordType::Char => Ok(Self::Char),
            _ => Err(CompilerError::Keyword("not a var type".into())),
        }
    }
}
/// the variable descript
#[derive(Debug)]
pub(super) struct VariableDesc {
    pub(super) var_type: VariableType,
    pub(super) vars: Vec<String>,
}

/// the variable and type pair
#[derive(Debug)]
pub(super) struct VariableTypePair {
    pub(super) var_type: VariableType,
    pub(super) var_name: String,
}
impl VariableTypePair {
    pub(super) fn new(var_type: VariableType, var_name: String) -> Self {
        Self { var_type, var_name }
    }
}

/// subroutine Type
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum SubroutineType {
    Constructor,
    Function,
    Method,
}

impl TryFrom<KeywordType> for SubroutineType {
    type Error = CompilerError;

    fn try_from(value: KeywordType) -> std::result::Result<Self, Self::Error> {
        match value {
            KeywordType::Constructor => Ok(Self::Constructor),
            KeywordType::Function => Ok(Self::Function),
            KeywordType::Method => Ok(Self::Method),
            _ => Err(CompilerError::Keyword("not a subroutine".into())),
        }
    }
}

/// keyword const
pub(super) enum KeywordConst {
    True,
    False,
    Null,
    This,
}
impl TryFrom<KeywordType> for KeywordConst {
    type Error = CompilerError;
    fn try_from(value: KeywordType) -> Result<Self, Self::Error> {
        match value {
            KeywordType::True => Ok(Self::True),
            KeywordType::False => Ok(Self::False),
            KeywordType::Null => Ok(Self::Null),
            KeywordType::This => Ok(Self::This),
            _ => Err(CompilerError::Keyword("not a keyword const".into())),
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub(super) enum ArithmeticOperator {
    /// +
    Plus,
    /// -
    Minus,
    /// *
    Multiply,
    /// /
    Divide,
    /// &
    And,
    /// |
    Or,
    /// <
    LessThan,
    /// >
    GreaterThan,
    /// =
    Equal,
    /// ~
    Not,
}

impl TryFrom<SymbolType> for ArithmeticOperator {
    type Error = CompilerError;

    fn try_from(value: SymbolType) -> Result<Self, Self::Error> {
        match value {
            SymbolType::Plus => Ok(Self::Plus),
            SymbolType::Minus => Ok(Self::Minus),
            SymbolType::Multiply => Ok(Self::Multiply),
            SymbolType::Divide => Ok(Self::Divide),
            SymbolType::And => Ok(Self::And),
            SymbolType::Or => Ok(Self::Or),
            SymbolType::LessThan => Ok(Self::LessThan),
            SymbolType::GreaterThan => Ok(Self::GreaterThan),
            SymbolType::Equal => Ok(Self::Equal),
            SymbolType::Not => Ok(Self::Not),
            _ => Err(CompilerError::Operator("not a arithmetic operator".into())),
        }
    }
}
